#ifndef __GLF_CORE_TLS_H_INCLUDED__
#define __GLF_CORE_TLS_H_INCLUDED__

#include <glf/config.h>
#include <glf/core/mem.h>
/**
   Usage cases:

   1) Global variable:

   // in a .h
   GLF_DECLARE_TLS(my_POD_type, the_name, extern);

   // in a .cpp
   GLF_DEFINE_TLS(my_POD_type, the_name) = { the_POD_compile_time_initializer };

   or if you do not want to initialize:

   GLF_DEFINE_TLS(my_POD_type, the_name);

   2) Static class variable:

   // in class .h
   class MyClass
   {
       ...

       GLF_DECLARE_TLS(my_POD_type, the_name, static);

       ...
   };

   // in class .cpp
   GLF_DEFINE_TLS(my_POD_type, MyClass::the_name) = { the_POD_compile_time_initializer };

   or if you do not want to initialize:

   GLF_DEFINE_TLS(my_POD_type, MyClass::the_name);

   3) static/anonymous namespace:

   // in a .cpp

   namespace {
   GLF_DECLARE_TLS(my_POD_type, the_name, extern);
   GLF_DEFINE_TLS(my_POD_type, the_name) = { the_POD_compile_time_initializer };
   }

   As above, the compile time initializer can be omitted; C-style static
   declaration is not supported. Use modern C++ syntax instead (which in the end
   does the same thing).

   4) static in a function

   Not supported.

 */

#if GLF_HAS_BUILTIN_COMPILER_TLS

namespace glf {

template <typename T>
class Tls {
public:

	// Exposed only for the sake of being a POD; do not use directly
	T mValue;

	T& get() { return mValue; }

	T* getPtr() { return &mValue; }
};

#define GLF_DECLARE_TLS(type, var, prefix) prefix GLF_BUILTIN_TLS_DECL ::glf::Tls<type> var
#define GLF_DEFINE_TLS(type, var) GLF_BUILTIN_TLS_DECL ::glf::Tls<type> var

}

#else

#include <glf/core/threadTraits.h>

namespace glf {

class TlsNode {
private:

	TlsNode* mNext;
	const void* mDefaultValue;
	int mAllocSize;
	TlsKey mSlot;

public:

	static TlsNode*& GetHead();

	template < typename T >
	TlsNode(const T* defaultValue) : mNext(GetHead()), mDefaultValue(defaultValue), mAllocSize(sizeof(T)) {
		GetHead() = this;
	}

	void Init() {
		allocTlsKey(mSlot);
		setTlsValue(mSlot, NULL);
	}

	void DeInit() {
		freeTlsKey(mSlot);
	}

	void* Alloc() const {
		void* value = GetValue();
		if(value == 0) {
			value = new unsigned char[mAllocSize];
			setTlsValue(mSlot, value);
			Memcpy(value, mDefaultValue, mAllocSize);
		}
		return value;
	}

	void DeAlloc() {
		void* value = GetValue();
		if(value) {
			delete[] (unsigned char*)value;
			setTlsValue(mSlot, NULL);
		}
	}

	void* GetValue(bool allocIfNull=false) const {
		void* tlsValue = getTlsValue(mSlot);
		if(allocIfNull && tlsValue == 0) {
			tlsValue = Alloc();
		}
		return tlsValue;
	}

	TlsNode* GetNext() const {
		return mNext;
	}
};

template <typename T>
class Tls : private TlsNode {
public:
	Tls(const T& defaultValue) : TlsNode(&defaultValue)	{
	}

	T& get() {
		void* value = GetValue(true);
		return *(T*)value;
	}

	T* getPtr() {
		void* value = GetValue();
		return (T*)value;
	}
};

#define GLF_DECLARE_TLS(type, var, prefix) \
	prefix const struct var##_DefaultValueType { type value; } var##_sDefaultValue; \
	prefix ::glf::Tls<type> var

#define GLF_DEFINE_TLS(type, var) \
	::glf::Tls<type> var = var##_sDefaultValue.value; \
	const var##_DefaultValueType var##_sDefaultValue

}

#endif

#endif //__GLF_CORE_TLS_H_INCLUDED__
